import json
import logging
import hmac
import hashlib
import time
from django.conf import settings
from django.utils import timezone
import requests
from requests.exceptions import RequestException

logger = logging.getLogger(__name__)


class WebhookService:
    """Webhook服务类，负责发送webhook通知"""
    
    @classmethod
    def trigger_webhook(cls, event_type, payload, project):
        """
        触发项目的webhook通知
        
        参数:
            event_type: 事件类型
            payload: 发送的数据
            project: 相关的项目对象
        """
        # 检查项目是否有激活的webhook且包含该事件类型
        # 使用annotate和ArrayAgg来处理JSON字段查询（SQLite兼容方案）
        webhooks = []
        for webhook in project.webhooks.filter(is_active=True):
            if event_type in webhook.events:
                webhooks.append(webhook)
        
        if not webhooks:
            logger.debug(f"没有激活的webhook配置了{event_type}事件类型，项目ID: {project.id}")
            return
        
        # 构建基本的webhook数据结构
        webhook_payload = {
            'event': event_type,
            'timestamp': int(time.time()),
            'project_id': str(project.id),
            'project_name': project.name,
            'data': payload
        }
        
        # 为每个匹配的webhook发送请求
        for webhook in webhooks:
            # 创建包含当前webhook渠道信息的payload副本
            webhook_with_channel = webhook_payload.copy()
            webhook_with_channel['channel'] = webhook.channel
            cls._send_webhook_request(webhook, webhook_with_channel)
    
    @classmethod
    def _send_webhook_request(cls, webhook, payload):
        """发送webhook请求"""
        headers = {
            'Content-Type': 'application/json',
            'X-Webhook-ID': str(webhook.id),
            'X-Webhook-Event': payload['event'],
        }
        
        # 如果设置了签名密钥，生成签名
        if webhook.secret:
            signature = cls._generate_signature(webhook.secret, payload)
            headers['X-Webhook-Signature'] = signature
        
        try:
            # 检查URL是否为测试地址，给出友好提示
            if cls._is_test_url(webhook.url):
                logger.warning(f"Webhook使用测试地址，跳过实际请求：{webhook.url}")
                # 更新最后触发时间但标记为测试
                webhook.last_triggered_at = timezone.now()
                webhook.save()
                return
            
            # 根据渠道类型转换消息格式
            final_payload = cls._convert_payload_for_channel(webhook, payload)
            
            # 发送webhook请求
            response = requests.post(
                webhook.url,
                json=final_payload,
                headers=headers,
                timeout=settings.WEBHOOK_TIMEOUT if hasattr(settings, 'WEBHOOK_TIMEOUT') else 10
            )
            
            # 更新最后触发时间
            webhook.last_triggered_at = timezone.now()
            webhook.save()
            
            # 记录响应状态
            if response.status_code >= 200 and response.status_code < 300:
                logger.info(f"Webhook请求成功，ID: {webhook.id}, 状态码: {response.status_code}")
            else:
                logger.warning(f"Webhook请求失败，ID: {webhook.id}, 状态码: {response.status_code}, 响应: {response.text}")
        
        except RequestException as e:
            logger.error(f"Webhook请求异常，ID: {webhook.id}, 错误: {str(e)}")
    
    @classmethod
    def _convert_payload_for_channel(cls, webhook, payload):
        """根据渠道类型转换消息格式"""
        # 飞书渠道需要特定的消息格式
        if webhook.channel == 'feishu' or 'open.feishu.cn' in webhook.url:
            return cls._convert_to_feishu_format(payload)
        
        # 其他渠道暂时保持原格式
        return payload
    
    @classmethod
    def _convert_to_feishu_format(cls, payload):
        """转换为飞书机器人消息格式"""
        # 提取关键信息
        event = payload.get('event', '系统通知')
        project_name = payload.get('project_name', '未知项目')
        
        # 尝试从两个位置获取summary数据
        # 1. 直接从 payload 中获取（定时任务直接传递）
        summary = payload.get('summary')
        # 2. 从 data 字段中获取（webhook 服务封装后）
        if not summary:
            data = payload.get('data', {})
            summary = data.get('summary', data)  # 如果没有summary，就把data作为summary
            # 也可能是data就是summary的情况
            if not summary.get('project_name') and data:
                summary = data
        
        # 构建消息标题和内容
        if event == 'pending_items_summary' or event == 'push':
            # 优先使用summary中的项目名称，如果没有则使用payload中的
            current_project_name = summary.get('project_name', project_name)
            title = f"📊 {current_project_name} - 待处理消息汇总"
            
            # 获取未处理消息数量，支持两种字段名
            pending_count = summary.get('pending_count', summary.get('total_pending_count', 0))
            
            # 获取开发者信息和平台链接
            developer_info = summary.get('developer_info', 'ip997.com')
            platform_link = summary.get('platform_link', '')
            
            # 构建内容元素列表
            elements = [
                {
                    "tag": "div",
                    "text": {
                        "content": f"**📁 项目名称：** {current_project_name}",
                        "tag": "lark_md"
                    }
                },
                {
                    "tag": "div",
                    "text": {
                        "content": f"**📊 未处理消息数量：** {pending_count}条",
                        "tag": "lark_md"
                    }
                },
                {
                    "tag": "div",
                    "text": {
                        "content": f"**👨💻 开发者信息：** {developer_info}",
                        "tag": "lark_md"
                    }
                }
            ]
            
            # 如果有平台链接，添加链接元素
            if platform_link:
                elements.append({
                    "tag": "action",
                    "actions": [
                        {
                            "tag": "button",
                            "text": {
                                "content": "🔗 访问平台",
                                "tag": "plain_text"
                            },
                            "type": "primary",
                            "url": platform_link
                        }
                    ]
                })
            
            # 构建飞书消息格式
            return {
                "msg_type": "interactive",
                "card": {
                    "elements": elements,
                    "header": {
                        "title": {
                            "content": "📊 待处理消息汇总",
                            "tag": "plain_text"
                        },
                        "template": "blue"
                    }
                }
            }
        else:
            # 其他事件类型的默认处理
            title = f"🔔 {project_name} - {event}"
            data = payload.get('data', payload)
            content_text = json.dumps(data, ensure_ascii=False, indent=2)
            
            # 默认消息格式
            return {
                "msg_type": "text",
                "content": {
                    "text": f"{title}\n\n{content_text}"
                }
            }
    
    @classmethod
    def _is_test_url(cls, url):
        """检查是否为测试URL"""
        test_patterns = [
            'test', 'example', 'dummy', 'fake', 'mock',
            '/test', '/example', 'mixed-push', 'push-only',
            'localhost', '127.0.0.1', 'webhook.site'
        ]
        
        return any(pattern in url.lower() for pattern in test_patterns)
        
    @classmethod
    def _generate_signature(cls, secret, payload):
        """生成webhook请求签名"""
        # 将payload转换为JSON字符串
        payload_str = json.dumps(payload, separators=(',', ':'), ensure_ascii=False)
        
        # 使用HMAC-SHA256生成签名
        signature = hmac.new(
            secret.encode('utf-8'),
            payload_str.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        
        return f'sha256={signature}'
    
    @classmethod
    def verify_webhook_signature(cls, request, secret):
        """
        验证接收到的webhook请求签名
        
        参数:
            request: Django请求对象
            secret: 签名密钥
            
        返回:
            bool: 签名是否有效
        """
        # 获取请求头中的签名
        signature_header = request.headers.get('X-Webhook-Signature', '')
        
        if not signature_header or not secret:
            return False
        
        # 解析签名
        if not signature_header.startswith('sha256='):
            return False
        
        received_signature = signature_header[7:]
        
        # 获取请求体
        try:
            payload = request.body.decode('utf-8')
        except UnicodeDecodeError:
            return False
        
        # 生成预期签名
        expected_signature = hmac.new(
            secret.encode('utf-8'),
            payload.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        
        # 比较签名
        return hmac.compare_digest(received_signature, expected_signature)


# 注册项目相关事件的webhook触发器
def trigger_project_created(project):
    """项目创建事件触发器"""
    payload = {
        'project_id': str(project.id),
        'project_name': project.name,
        'description': project.description,
        'created_by': project.created_by.username,
        'created_at': project.created_at.isoformat(),
    }
    WebhookService.trigger_webhook('project_created', payload, project)

def trigger_project_updated(project, updated_fields=None):
    """项目更新事件触发器"""
    payload = {
        'project_id': str(project.id),
        'project_name': project.name,
        'description': project.description,
        'updated_at': project.updated_at.isoformat(),
        'updated_fields': updated_fields or []
    }
    WebhookService.trigger_webhook('project_updated', payload, project)

def trigger_project_deleted(project_id, project_name):
    """项目删除事件触发器"""
    # 注意：项目已删除，这里使用字典而不是项目对象
    from projects.models import Project
    # 尝试获取项目（如果尚未完全删除）
    try:
        project = Project.objects.get(id=project_id)
        WebhookService.trigger_webhook('project_deleted', {
            'project_id': str(project_id),
            'project_name': project_name
        }, project)
    except Project.DoesNotExist:
        logger.warning(f"无法触发项目删除webhook，项目ID: {project_id} 不存在")

def trigger_member_added(project, user, role):
    """成员添加事件触发器"""
    payload = {
        'user_id': str(user.id),
        'username': user.username,
        'role': role,
        'joined_at': timezone.now().isoformat()
    }
    WebhookService.trigger_webhook('member_added', payload, project)

def trigger_member_removed(project, user, role):
    """成员移除事件触发器"""
    payload = {
        'user_id': str(user.id),
        'username': user.username,
        'role': role,
        'removed_at': timezone.now().isoformat()
    }
    WebhookService.trigger_webhook('member_removed', payload, project)

def trigger_member_role_changed(project, user, old_role, new_role):
    """成员角色变更事件触发器"""
    payload = {
        'user_id': str(user.id),
        'username': user.username,
        'old_role': old_role,
        'new_role': new_role,
        'changed_at': timezone.now().isoformat()
    }
    WebhookService.trigger_webhook('member_role_changed', payload, project)

def trigger_tag_added(project, tag):
    """标签添加事件触发器"""
    payload = {
        'tag_id': str(tag.id),
        'tag_name': tag.name,
        'tag_color': tag.color,
        'created_at': tag.created_at.isoformat() if hasattr(tag, 'created_at') else timezone.now().isoformat()
    }
    WebhookService.trigger_webhook('tag_added', payload, project)

def trigger_tag_updated(project, tag, updated_fields=None):
    """标签更新事件触发器"""
    payload = {
        'tag_id': str(tag.id),
        'tag_name': tag.name,
        'tag_color': tag.color,
        'updated_at': timezone.now().isoformat(),
        'updated_fields': updated_fields or []
    }
    WebhookService.trigger_webhook('tag_updated', payload, project)

def trigger_tag_deleted(project, tag_id, tag_name):
    """标签删除事件触发器"""
    payload = {
        'tag_id': str(tag_id),
        'tag_name': tag_name,
        'deleted_at': timezone.now().isoformat()
    }
    WebhookService.trigger_webhook('tag_deleted', payload, project)

def trigger_announcement_sent(announcement):
    """触发公告发送事件"""
    payload = {
        'event': 'announcement.sent',
        'announcement': {
            'id': str(announcement.id),
            'title': announcement.title,
            'content': announcement.content,
            'created_by': announcement.created_by.username,
            'created_at': announcement.created_at.isoformat(),
            'tags': [tag.name for tag in announcement.tags.all()]
        }
    }

    # 获取与公告标签相关的项目
    from projects.models import Project
    if announcement.tags.exists():
        # 如果有指定标签，只通知相关项目
        target_projects = Project.objects.filter(
            tags__in=announcement.tags.all()
        ).distinct()
    else:
        # 如果没有指定标签，通知所有项目
        target_projects = Project.objects.all()

    # 为每个项目触发webhook
    for project in target_projects:
        WebhookService.trigger_webhook('announcement_sent', payload, project)